/**
 * @fileoverview 文档支持 - 右侧菜单悬浮效果，以及锚点滚动效果
 */
(function(){

	
	var _win = window,
		_doc = document;
	
	/**
	 * 初始化一个浮动对象，使其在一定区域内随屏幕滚动浮动显示（尽可能在屏幕区域内显示）。
	 * @author	梁栋[pillar0514@gmail.com]
	 * @param {HTMLElement} el 操作的目标元素
	 * @param {HTMLElement} options 选项
	 * @example
		var _rightfloat = new clsAreaFloat($$('.doc .methods')[0]);
		var activefloat = _rightfloat.active.bind(_rightfloat);
		_win.addEvents({
			'scroll':activefloat,
			'resize':activefloat
		});
		_rightfloat.active();
	 *
	 */
	var clsAreaFloat = new Class({
		Implements:[Options],
		options:{
			target:null,						//浮动目标对象
			area:null,							//浮动对象的区域参照对象
			duration:300,						//动画延时
			timeout:2000,						//心跳检测延时
			checkonce:{target:false,area:false},//只检查一次，如果浮动元素或者容器元素的高度都是不变的，可以启用对应的值
			targetStyle:{'margin-top':'top'},	//要操作的浮动对象的样式 {'对应的CSS值':'相对值','对应的CSS值':'相对值'}，可用相对值：['top','bottom','left','right']
			margin:[0,0,0,0]					//浮动对象与区域参照对象的边距 [上，右，下，左]
		},
		//初始化
		initialize:function(el,options){
			this.setOptions(options);
			var ops = this.options;
			this.pos = {};
			this.target = $(el)||$(ops.target);
			if(this.target){
				this.area = $(ops.area) || this.target.getParent();	//默认取父元素为区域参照对象
			}
			if(this.target && this.area){this.enable = true}
			else {return;}
			this.fx = new Fx.Morph(this.target,{fps:50,duration:ops.duration});
		},
		//立即检查各项数据，修正目标的位置
		check:function(){
			if(!this.enable)return;
			this.compute();
			Object.each(this.options.targetStyle, function(prop,key){
				this.target.setStyle(key,this.pos[prop]);
			},this);
		},
		//心跳检测 - 每调用一次此函数可以让动画定时器存在时间延长一段时间。
		active:function(){
			if(!this.enable)return;
			this.animate();
			var ops = this.options,
				atime = ops.timeout > ops.duration * 2 ? ops.timeout : ops.duration * 2 ;
			if (this.atimer) {
				clearTimeout(this.atimer);
				delete this.atimer;
			}
			this.atimer = setTimeout(function(){
				clearInterval(this.timer);
				delete this.timer;
			}.bind(this),atime);
		},
		//加载动画定时器。
		animate:function(){
			if(!this.enable)return;
			if(!this.timer){
				this._animatepos();
				this.timer = setInterval(this._animatepos.bind(this),this.options.duration);
			}
		},
		//计算目标当前应该所在的相对位置
		compute:function(){
			if(!this.enable)return;
			this.sinfo = this._getScreenInfo();
			this.tinfo = this._getElementInfo(this.target);
			this.ainfo = this._getElementInfo(this.area);
			this._computepos('width','left','right',3,1,this.sinfo,this.tinfo,this.ainfo);		//检查横向
			this._computepos('height','top','bottom',0,2,this.sinfo,this.tinfo,this.ainfo);		//检查纵向
		},
		//通过动画修正目标的位置
		_animatepos:function(){
			this.compute();
			var pos = this.pos,
				tinfo = this.tinfo,
				ainfo = this.ainfo;
			var nowpos = {
				'top' : tinfo.top - ainfo.top,
				'left' : tinfo.left - ainfo.left
			};
			nowpos.bottom = tinfo.height + nowpos.top;
			nowpos.right = tinfo.width + nowpos.left;
			var fxpara = {};
			Object.each(this.options.targetStyle, function(prop,key){
				fxpara[key] = [nowpos[prop],pos[prop]];
			},this);
			this.fx.cancel().start(fxpara);	
		},
		/**
		 * 计算横纵边距 - 核心算法
		 * @param {string} prop	 对应元素大小相关属性 	['wdith','height']
		 * @param {string} d1	 对应元素位置相关属性 	['left','top']	
		 * @param {string} d2	 对应元素位置相关属性 	['right','bottom']	
		 * @param {number} n1	 对应margin数组下标	 	[3,0] (左，上)
		 * @param {number} n2	 对应margin数组下标	 	[1,2] (右，下)
		 * @param {object} sinfo 窗口大小与位置信息
		 * @param {object} tinfo 目标元素大小与位置信息
		 * @param {object} ainfo 区域参照元素的大小与位置信息
		 */
		_computepos:function(prop,d1,d2,n1,n2,sinfo,tinfo,ainfo){
			var margin = this.options.margin,
				pos = {},
				min = margin[n1],
				max = ainfo[prop]  - tinfo[prop] - margin[n2],
				pre = this['pre'+d1] || min;
			if (tinfo[prop] + margin[n2] + margin[n1] < ainfo[prop]) {									//以纵向为例，如果目标元素高度小于区域元素高度，才执行计算
				pos[d1] = 	( sinfo[prop] > tinfo[prop] || sinfo[d1] < tinfo[d1] ) ? 					//如果窗口高度大于目标元素高度，或者窗口上限坐标小于目标元素上限坐标
							sinfo[d1] - ainfo[d1] : 													//则目标元素上边距应当为（窗口上限坐标 - 区域上限坐标）
							( sinfo[d2] > tinfo[d2] ? sinfo[d2] - tinfo[prop] - ainfo[d1] : pre ) ;     //否则检查窗口下限与目标元素下限，如果超出，则目标元素上边距为 （窗口下限 - 区域上限 - 目标元素高度）
				pos[d1] = pos[d1].limit( min, max );													//限制计算出的边距值在最大值和最小值之间。
			}else{																						//否则目标元素高度为最小值
				pos[d1] = min;
			}
			this['pre'+d1] = pos[d1];
			this.pos[d1] = pos[d1];
			this.pos[d2] = ainfo[prop] - tinfo[prop] - pos[d1];
		},
		//获取元素相对于页面的位置偏移，与大小
		_getElementInfo:function(el){
			var _xy = el.getPosition(),
				_size = el.getSize();
			return {
				left:_xy.x,
				right:_xy.x + _size.x,
				top:_xy.y,
				bottom:_xy.y + _size.y,
				width:_size.x,
				height:_size.y
			};
		},
		//获取窗口相对于页面的位置
		_getScreenInfo:function(){
			var _wscroll = _win.getScroll(),
				_wsize = _win.getSize();
			return {
				top:_wscroll.y,
				bottom:_wscroll.y+_wsize.y,
				left:_wscroll.x,
				right:_wscroll.x+_wsize.x,
				width:_wsize.x,
				height:_wsize.y
			};
		}
	});

	_win.addEvent('domready',function(){

		var SmoothScroll = new Fx.SmoothScroll();
		var _rightfloat = new clsAreaFloat($$('.doc .methods')[0]);
		
		//绑定事件
		var activefloat = _rightfloat.active.bind(_rightfloat);
		_win.addEvents({
			'scroll':activefloat,
			'resize':activefloat,
			'beforeunload':function(){
				this.removeEvents();
			}
		});
		_rightfloat.active();
	});
	
})();